import math
import pygame
from didiball.Vector import Vector
import didiball.Physics2D as p2d

class PassiveSprite(pygame.sprite.Sprite):
    def __init__( self, posxy, speed_angle, speed_magnitude, surface, dynamics):
        pygame.sprite.Sprite.__init__(self)
        pos_angle = p2d.calc_angle_from_O(posxy)
        pos_magnitude = p2d.calc_distance_from_O(posxy)
        self.__pos = Vector(pos_angle,pos_magnitude)
        self.__speed = Vector(speed_angle,speed_magnitude)
        self.__surface = surface
        self.__width,self.__height = self.get_surface().get_size()
        self.__radius = (self.get_width()+self.get_height())*0.25
        self.__mass = float(dynamics['mass'])
        self.__max_speed = float(dynamics['max_speed'])
        self.__dynamics = dynamics
        self.__friction = float(dynamics['friction'])
    
    def calc_distance_to(self,other_sprite):
        return p2d.calc_distance(self.get_pos_xy(),other_sprite.get_pos_xy())
    
    def get_pos(self):
        return self.__pos    
    def get_pos_angle(self):
        return self.get_pos().get_angle()    
    def get_pos_magnitude(self):
        return self.get_pos().get_magnitude()     
    def get_pos_xy(self):
        return self.get_pos().get_xy()  
    def get_surface(self):
        return self.__surface
    def get_width(self):
        return self.__width
    def get_height(self):
        return self.__height
    def get_radius(self):
        return self.__radius
    def get_speed(self):
        return self.__speed
    def get_speed_angle(self):
        return self.get_speed().get_angle()
    def get_speed_magnitude(self):
        return self.get_speed().get_magnitude()
    def get_speed_xy(self):
        return self.get_speed().get_xy()
    def get_mass(self):
        return self.__mass
    def get_max_speed(self):
        return self.__max_speed
      
    def set_pos(self,pos):
        self.__pos = pos
    def set_pos_angle(self,angle):
        self.get_pos().set_angle(angle)
    def set_pos_magnitude(self,magnitude):
        self.get_pos().set_magnitude(magnitude)
    def set_pos_xy(self,pos):
        self.get_pos().set_xy(pos)
    def set_surface(self,surface):
        self.__surface = surface
    def set_width(self,width):
        self.__width = width
    def set_height(self,height):
        self.__height = height
    def set_radius(self,radius):
        self.__radius = radius
    def set_speed(self,speed):
        self.__speed = speed
    def set_speed_angle(self,angle):
        self.get_speed().set_angle(angle)
    def set_speed_magnitude(self,magnitude):
        self.get_speed().set_magnitude(magnitude)
    def set_speed_xy(self,speed):
        self.get_speed().set_xy(speed)
    def set_mass(self,m):
        self.__mass = m
    def set_max_speed(self,max_speed):
        self.__max_speed = max_speed
        
    def get_rectangular_coords( self):
        px,py = self.get_pos_xy()
        return (px-(self.get_width()*0.5), py-(self.get_height()*0.5), self.get_width(),self.get_height())

    def get_rect(self,rect_coords=None):
        if rect_coords==None:
                rect_coords= self.get_rectangular_coords()
        return pygame.Rect(rect_coords[0],rect_coords[1],rect_coords[2],rect_coords[3])

    def get_friction(self):
        return self.__friction
        
    def set_friction(self,friction):
        self.__friction = friction
                
    # apply friction
    def friction( self, tick_ms):
        if self.get_speed_magnitude()>0:
            self.set_speed_magnitude(self.get_speed_magnitude()-self.get_friction()*tick_ms)
        if self.get_speed_magnitude()<0:
            self.set_speed_magnitude(0)
    
    def is_moving_towards( self, other):
        if 10 > (p2d.calc_angle(self.get_pos().get_xy(),other.get_pos().get_xy())-self.get_speed_angle()) > -10:
            return True
        return False
 
    # modify vector (apply friction)
    def mod( self, tick_ms):
        self.friction(tick_ms)
        
    def move( self, tick_ms, pitch):
    #print "speed: {0}".format(self.get_speed()) 
        # calc new position
        new_pos = self.get_pos()+tick_ms*self.get_speed()
        px,py = new_pos.get_xy()
       
        #check for collisions with edges 
        gs= pitch.get_goals()[0].top
        ge= pitch.get_goals()[0].top+pitch.get_goals()[0].height
        ll= pitch.get_rect().left
        lr= pitch.get_rect().left+pitch.get_rect().width
        lt= pitch.get_rect().top
        lb= pitch.get_rect().top+pitch.get_rect().height

        if not (gs<py<ge):
            #  alter speed_angle by 180-speed_angle if it's hit the horizontal limits
            if px < ll+self.get_width()*0.5:
                px = ll+self.get_width()*0.5
                self.set_speed_angle (180-self.get_speed_angle())
            elif px > lr-self.get_width()*0.5:
                px = lr-self.get_width()*0.5
                self.set_speed_angle (180-self.get_speed_angle())
            
        #  alter speed_angle by 360-speed_angle if it's hit the vertical limits
        if (py < lt+self.get_height()*0.5):
            py = lt+self.get_height()*0.5
            self.set_speed_angle(360-self.get_speed_angle())   
        elif (py > lb-self.get_height()*0.5):
            py = lb-self.get_height()*0.5
            self.set_speed_angle(360-self.get_speed_angle())
            
        self.set_pos_xy((px,py))
        
        # return the half of the pitch which the player/ball resides
        if px < ll-self.get_width()*0.5:
            return 1
        elif px > lr+self.get_width()*0.5:
            return 2
        
        return 0     
    
    def collision(self, B, tick_ms):
    
        # if self is stopped, switch around
        A = self
        if A.get_speed_magnitude()==0:
            (A,B)=(B,A)
        
        # vector
        S = A.get_speed()-B.get_speed()
        
        # distance - radii
        dist = p2d.calc_distance( A.get_pos_xy(), B.get_pos_xy())
        sumRadii = A.get_radius() + B.get_radius()
        dist -= sumRadii
        
        # if combined velocity doesnt cover the distance, return false right away
        if S.get_magnitude()*tick_ms < dist:
            return False
            
        N = S.copy()
        N.normalize()
        # C is the vector of the 2 positions
        C = B.get_pos()-A.get_pos()
        # D is the product of the vector and the distance
        D = N*C
        
        # If it's negative, return false
        if D <= 0:
            return False

        # I'm getting lost around about now.
        # That's what you get for copy-pasting code.
        F = C.get_magnitude()**2-D**2
        sumRadiiSquared = sumRadii**2
        if F >= sumRadiiSquared :
            return False

        T = sumRadiiSquared - F
        if T<0:
            return False
            
        dist = D - math.sqrt(T)

        # If the velocity doesnt cover the distance ..
        if S.get_magnitude()*tick_ms < dist:
            return False
        else:
            def handler():
                self.handle_collision(A,B,C)
            return handler

    def handle_collision(self,A,B,C):
        N = C.copy()
        N.normalize()
        a1 = A.get_speed()*N
        a2 = B.get_speed()*N
        
        # calc new vectors
        P = (2*(a1-a2))/(A.get_mass()+B.get_mass())
        newA = A.get_speed() - P*B.get_mass()*N
        newB = B.get_speed() + P*A.get_mass()*N
        
        A.set_speed(newA)
        B.set_speed(newB)

        #limit to max speeds
        if A.get_speed_magnitude()>A.get_max_speed():
            A.set_speed_magnitude(A.get_max_speed())
        if B.get_speed_magnitude()>B.get_max_speed():
            B.set_speed_magnitude(B.get_max_speed())
        
        return True

    def calc_adjusted_coords( self, display_surface, shifting_plane):
        return shifting_plane.adjust_coords(self.get_rectangular_coords())

    def unblit_to_shifting_plane( self, display_surface, shifting_plane,bg):
        display_surface.blit( bg, shifting_plane.adjust_coords(self.get_rectangular_coords()), shifting_plane.adjust_coords(self.get_rectangular_coords()))

    def blit_to_shifting_plane( self, display_surface, shifting_plane):
        display_surface.blit( self.get_surface(), shifting_plane.adjust_coords(self.get_rectangular_coords()))

    def unblit_to( self, surface, bg):
        surface.blit( bg, self.get_rectangular_coords())
        
    def blit_to( self, surface):
        surface.blit( self.get_surface(), self.get_rectangular_coords())

